/*
 * Copyright 2007-2010 Jeremy Brown.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author <a href="http://jeremy-brown.com">Jeremy Brown</a>
 */

package groovy.prefuse.demos

import prefuse.*
import prefuse.action.*
import prefuse.action.assignment.*
import prefuse.action.filter.*
import prefuse.action.layout.*
import prefuse.action.layout.graph.*
import prefuse.activity.*
import prefuse.controls.*
import prefuse.data.*
import prefuse.data.expression.*
import prefuse.data.expression.parser.*
import prefuse.data.query.*
import prefuse.render.RendererFactory as PrefuseRendererFactory
import prefuse.render.*
import prefuse.util.*
import prefuse.util.ui.*
import prefuse.visual.*
import prefuse.visual.expression.*
import prefuse.visual.sort.*

import groovy.swing.*
import groovy.prefuse.*
import groovy.prefuse.factory.*

import java.awt.*
import java.awt.event.*
import java.awt.geom.*
import javax.swing.*
import java.text.*

/**
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class Congress
{

	static PrefuseBuilder swing
	static String congressTitle = "\u00a2 o n g r e s s"
	static String congressString = "\u00a2ongress"
	
    private static final String TOTAL_RECEIPTS = "Total Receipts";
    private static final String RECEIPTS = "Total Receipts";
    
    private static String m_title = congressString;
    private static String m_totalStr;
    private static double m_totalMoney = 1000000000;
    private static int m_totalPeople = 10000;
    
    private static Rectangle2D m_dataB = new Rectangle2D.Double();
    private static Rectangle2D m_xlabB = new Rectangle2D.Double();
    private static Rectangle2D m_ylabB = new Rectangle2D.Double();


	public static void main(String[] args)
	{
		UILib.setPlatformLookAndFeel();

		swing = new PrefuseBuilder()
		swing.registerPrefuse()
		
		demo()
	}
        	
	public static void demo()
	{
        //super(new BorderLayout());
        
        // --------------------------------------------------------------------
        // STEP 1: setup the visualized data
        
        final String group = "by_state";
		
        NumberFormat nf = NumberFormat.getCurrencyInstance();
        nf.setMaximumFractionDigits(0);
		
		swing.build()
		{
			frame(id:"frame", title:congressTitle, defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE)
			{
				panel(id:"panel")
				{
					borderLayout()
					
					hbox(constraints:BorderLayout.NORTH)
					{
						hstrut(width:5)
						pfJFastLabel(m_title, id:"details", preferredSize:[75,20], verticalAlignment:SwingConstants.BOTTOM, font:FontLib.getFont("Tahoma", 18))
						hglue()
						hstrut(width:5)
						pfJFastLabel(m_totalPeople+" Candidates: "+m_totalMoney, id:"total", preferredSize:[500,20], horizontalAlignment:SwingConstants.RIGHT, verticalAlignment:SwingConstants.BOTTOM, font:FontLib.getFont("Tahoma", 16))
						hstrut(width:5)
					}
					
					pfDisplay(constraints:BorderLayout.CENTER, id:"display", size:[700,450], border:BorderFactory.createEmptyBorder(10,10,10,10))
					{
						pfVisualization(id:"vis", defaultGroup:"by_state")
						{
							pfTable(file:"data/fec.txt", hasHeader:true, id:"table", visualID:"visualTable", predicate:"[${TOTAL_RECEIPTS}] >= 100000")
							pfVisualTable(visualTable)
							{
								pfColumn(name:"Senate", expression:"District <= 0")
								pfColumn("label", expression:"CONCAT(CAP(Candidate), ' (', CAP([Party Designation]), '-', [State Code], ') ', Year, ': \$', FORMAT([Total Receipts],2))")
							}
							
							pfRangeQueryBinding(tupleSet:visualTable, field:RECEIPTS, id:"receiptsQ", valueRange:[0,65000000,0,65000000])
							pfListQueryBinding(tupleSet:visualTable, field:"Year", id:"yearsQ")
							pfSearchQueryBinding(tupleSet:visualTable, field:"Candidate", id:"searchQ")

							pfAxisLayout(id:"xaxis", name:"xaxis", dataField:"State Code", axis:Constants.X_AXIS, filter:VisiblePredicate.TRUE, layoutBounds:m_dataB)
							pfAxisLayout(id:"yaxis", name:"yaxis", dataField:RECEIPTS, axis:Constants.Y_AXIS, filter:VisiblePredicate.TRUE, layoutBounds:m_dataB, rangeModel:receiptsQ.getModel())
							pfAxisLabelLayout(id:"ylabels", group:"ylab", axisLayout:yaxis, layoutBounds:m_ylabB, numberFormat:nf)
							pfAxisLabelLayout(id:"xlabels", name:"xlabels", group:"xlab", axisLayout:xaxis, layoutBounds:m_xlabB, spacing:15)
							pfDataColorAction(id:"color", dataField:"Party", dataType:Constants.ORDINAL, field:VisualItem.STROKECOLOR, palette:[ColorLib.rgb(150,150,255), [255,150,150], [180,180,180]]) // dems = blue, reps = red, other = gray
							pfDataShapeAction(id:"shape", dataField:"Senate", shapes:[Constants.SHAPE_RECTANGLE, Constants.SHAPE_DIAMOND])

							pfShapeRenderer(id:"sr")
							pfAxisRenderer(id:"arY", align:Constants.RIGHT, valign:Constants.TOP)
							pfAxisRenderer(id:"arX", align:Constants.CENTER, valign:Constants.FAR_BOTTOM)

							pfRendererFactory(getRenderer:{item->
								return	item.isInGroup("ylab") ? arY :
										item.isInGroup("xlab") ? arX :
										sr
							})
							
							pfGroupAction(id:"cntr", group:group,
								run:{frac->
									double dtotal = 0;
									int count = 0;
									VisualItem item = null;
									Iterator items = vis.visibleItems(group);
									while(items.hasNext())
									{
										item = (VisualItem)items.next();
										dtotal += item.getDouble("Total Receipts");
										++count;
									}
									m_totalMoney = dtotal;
									m_totalPeople = count;

									if(count == 1)
									{
										m_totalStr = item.getString("label");
									}
									else
									{
										m_totalStr = count + " Candidates receiving " + NumberFormat.getCurrencyInstance().format(dtotal);
									}
									total.setText(m_totalStr);
								}
							)
							
							pfActionList(id:"draw", name:"draw")
							{
								pfAction(cntr)
								pfAction(color)
								pfAction(shape)
								pfAction(xaxis)
								pfAction(yaxis)
								pfAction(ylabels)
								pfColorAction(group:group, field:VisualItem.FILLCOLOR, defaultColor:0) //defaultColor:0 means transparent (alpha=0)
								pfRepaintAction()
							}

							pfPredicate(new AndPredicate(), id:"filter")
							{
								pfPredicate(searchQ.getPredicate())
								pfPredicate(yearsQ.getPredicate())
								pfPredicate(receiptsQ.getPredicate())
								pfExpressionListener([update:{src-> vis.run("update")}] as UpdateListener, id:"updateListener")
							}
							
							pfActionList(id:"update", name:"update")
							{
								pfVisibilityFilter(group:group, predicate:filter)
								pfAction(cntr)
								pfAction(xaxis)
								pfAction(yaxis)
								pfAction(ylabels)
								pfRepaintAction()
							}
						}
						
						pfItemSorter(score:{item, score->
							if(item.isInGroup(group))
							{
								score += item.getInt(TOTAL_RECEIPTS)
							}
							return score
						})
						
						pfToolTipControl("label")
						
						
						pfControl(id:"hoverc", 
							itemEntered: {item, evt->
								if(item.isInGroup(group))
								{
									total.setText(item.getString("label"));
									item.setFillColor(item.getStrokeColor());
									item.setStrokeColor(ColorLib.rgb(0,0,0));
									item.getVisualization().repaint();
								}
							},
							itemExited: {item, evt->
								if(item.isInGroup(group))
								{
									total.setText(m_totalStr);
									item.setFillColor(item.getEndFillColor());
									item.setStrokeColor(item.getEndStrokeColor());
									item.getVisualization().repaint();
								}
							}
						)
						
						pfComponentListener(componentResized:{displayLayout()})
					}
					
					hbox(constraints:BorderLayout.SOUTH, id:"radioBox")
					{
						hstrut(width:5)
						pfJSearchPanel(searchQ, labelText:"Candidate: ", border:BorderFactory.createEmptyBorder(5,5,5,0), id:"searcher")
						hglue()
						hstrut(width:5)
						widget(yearsQ.createRadioGroup())
						hstrut(width:16)
					}
					UILib.setFont(radioBox, FontLib.getFont("Tahoma", 15));
					
					pfJRangeVSlider(receiptsQ, id:"slider", thumbColor:null, minExtent:150000, foreground:Color.LIGHT_GRAY, constraints:BorderLayout.EAST,
						mousePressed:{e->display.setHighQuality(false)}, 
						mouseReleased:{e->display.setHighQuality(true); display.repaint()}
					)
					
					pfComponentListener(updateListener)
				}
		        UILib.setColor(panel, ColorLib.getColor(255,255,255), Color.GRAY)
			}
		}
		



        
        // --------------------------------------------------------------------        
        // STEP 2: launching the visualization

		swing.frame.pack();
		swing.frame.show()
		
        swing.vis.run("draw");
        swing.vis.run("xlabels");
		
        displayLayout();
    }
    
    public static void displayLayout() {
        Insets i = swing.display.getInsets();
        int w = swing.display.getWidth();
        int h = swing.display.getHeight();
        int iw = i.left+i.right;
        int ih = i.top+i.bottom;
        int aw = 85;
        int ah = 15;
        
        m_dataB.setRect(i.left, i.top, w-iw-aw, h-ih-ah);
        m_xlabB.setRect(i.left, h-ah-i.bottom, w-iw-aw, ah-10);
        m_ylabB.setRect(i.left, i.top, w-iw, h-ih-ah);
        
        swing.vis.run("update");
        swing.vis.run("xlabels");
    }
} // end of class ScatterPlot
